//
// Created by edward on 23-3-17.
//

#include "Texture.h"
#include "Shader.h"
#include <iostream>

std::unordered_map<std::string, unsigned int> Texture::mp;

Texture::Texture(const std::string &path, const std::string &name, Filter f, Wrap w)
    : name_(name)
    {
    if (auto iter = mp.find(path); iter != mp.end()) {
        id_ = iter->second;
    } else {
//        std::cout << path << std::endl;
        int width, height, nrChannels;

        glGenTextures(1, &id_);
        glBindTexture(GL_TEXTURE_2D, id_);

        // set the texture wrapping/filtering options (on the currently bound texture object)
        switch (w) {
            case Wrap::REPEAT:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
                break;
            case Wrap::MIRRORED_REPEAT:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
                break;
            case Wrap::CLAMP_TO_EDGE:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                break;
            case Wrap::CLAMP_TO_BORDER:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
                break;
        }

        switch (f) {
            case Filter::LINEAR:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                break;
            case Filter::NEAREST:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                break;
        }

        /// flip y axis
        /// assmip have done by aiProcess_FlipUVs
        stbi_set_flip_vertically_on_load(true);

        unsigned char *data;

        Type t;
        std::string postfix = path.substr(path.find_last_of('.') + 1);
        if (postfix == "JPG" || postfix == "jpg") {
            t = Type::JPG;
        } else if (postfix == "PNG" || postfix == "png") {
            t = Type::PNG;
        }

        switch (t) {
            case Type::JPG:
                data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);
                if (data) {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
                    glGenerateMipmap(GL_TEXTURE_2D);
                } else {
                    std::cout << "Failed to load texture" << std::endl;
                }
                break;
            case Type::PNG:
                data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);
                if (data) {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
                    glGenerateMipmap(GL_TEXTURE_2D);
                } else {
                    std::cout << "Failed to load texture" << std::endl;
                }
                break;
            default: //default for jpg
                data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);
                if (data) {
                    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
                    glGenerateMipmap(GL_TEXTURE_2D);
                } else {
                    std::cout << "Failed to load texture" << std::endl;
                }
                break;
        }

        stbi_image_free(data);

        mp[path] = id_;
    }
}

void Texture::use(Shader *shader, unsigned unit) {
    glActiveTexture(GL_TEXTURE0 + unit);
    glBindTexture(GL_TEXTURE_2D, id_);
    shader->set("material." + name_ + std::to_string(unit), static_cast<int>(unit));
}
